#include "Lite_Queue.h"

/*
* @ brief : Create message queue.
* @ param : {size_t     } item_num : The number of list items in the queue.
            {size_t     } item_size: The size of each list item, unit: bytes.
* @ return: {LiteQueue *} queue    : Message queue handle pointer.
* @ author: bagy.
* @ note  : Create a queue and initialize the queue items to 0, with the head and tail pointers pointing to the starting position of the list items.
*/
LiteQueue *xLiteQueue_Create(size_t item_num, size_t item_size, LiteQueue_Callback_t callback)
{
    if((item_num < 1) || (item_size < 1))
        return NULL;

    LiteQueue *queue = (LiteQueue *)malloc(sizeof(LiteQueue) + item_num * item_size);

    if(queue == NULL)
    {
        log_i("LiteQueue malloc failed.\r\n");
        return NULL;
    }

    memset((uint8_t *)queue, 0, sizeof(LiteQueue) + item_num * item_size);

    queue->head = (uint8_t *)((uint8_t *)queue + sizeof(LiteQueue));
    queue->tail = queue->head;
    queue->item_num = item_num;
    queue->item_size = item_size;
    queue->queue_read_lock = LITE_QUEUE_UNLOCK;
    queue->queue_write_lock = LITE_QUEUE_UNLOCK;
    queue->unread_item = 0;
    queue->LiteQueue_Callback = callback;

    return queue;
}

/*
* @ brief : Write data to the queue.
* @ param : {LiteQueue      *} queue    : Message queue handle pointer.
            {uint8_t        *} buff     : Data to be written to the queue.
            {QueueWriteMode  } WriteMode: Strategies for writing to the queue.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ author: bagy.
* @ note  : Writing data when the queue is full will automatically overwrite the first frame of data.
*/
LiteQueue_Status xWrite_To_LiteQueue(LiteQueue *queue, uint8_t *buff, QueueWriteMode WriteMode)
{
    if((queue == NULL) || (buff == NULL))
        return LITE_QUEUE_ERR;

    LITE_QUEUE_WRITE_LOCK(queue);

    if(isLiteQueue_Full(queue) == LITE_QUEUE_FULL)
    {
        if(queue->LiteQueue_Callback != NULL)
            queue->LiteQueue_Callback(LITE_QUEUE_FULL);

        if(WriteMode == QUEUE_SEND_TO_BACK)
            log_i("The queue is full, overwriting the data that first entered the queue.\n");
        else
        {
            LITE_QUEUE_WRITE_UNLOCK(queue);
            log_i("Queue is full, discard current frame.\n");
            return LITE_QUEUE_FULL;  
        }  
    }

    memcpy(queue->tail, buff, queue->item_size);

    if(queue->tail == (uint8_t *)queue + sizeof(LiteQueue) + (queue->item_num - 1) * queue->item_size)
        queue->tail = (uint8_t *)queue + sizeof(LiteQueue);
    else 
        queue->tail += queue->item_size;

    queue->unread_item++;
    if(queue->unread_item >= queue->item_num)
        queue->unread_item = queue->item_num;

    LITE_QUEUE_WRITE_UNLOCK(queue);

    return LITE_QUEUE_OK;
}

/*
* @ brief : Read data from queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
            {uint8_t        *} buff : Data to be read from the queue.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ author: bagy.
* @ note  : Read data starting from the position of the head pointer and save it to the buff.
*/
LiteQueue_Status Read_From_LiteQueue(LiteQueue *queue, uint8_t *buff)
{
    if((queue == NULL) || (buff == NULL) || (isLiteQueue_Empty(queue) == LITE_QUEUE_EMPTY))
        return LITE_QUEUE_ERR;

    LITE_QUEUE_READ_LOCK(queue);

    memcpy(buff, queue->head, queue->item_size);  

    if(queue->head == (uint8_t *)queue + sizeof(LiteQueue) + (queue->item_num - 1) * queue->item_size)
        queue->head = (uint8_t *)queue + sizeof(LiteQueue);
    else 
        queue->head += queue->item_size;

    queue->unread_item--;

    LITE_QUEUE_READ_UNLOCK(queue);

    return LITE_QUEUE_OK;  
}

/*
* @ brief : Determine whether the queue is empty.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ author: bagy.
* @ note  : Determine whether the queue is empty based on the comparison between the number of remaining unread list items and the total list 
            items of the queue. If the number of remaining unread list items is 0, it means that the queue is empty and the queue is not empty.
*/
inline LiteQueue_Status isLiteQueue_Empty(LiteQueue *queue)
{
    if(queue == NULL)
        return LITE_QUEUE_ERR;

    if(!queue->unread_item)
        return LITE_QUEUE_EMPTY;
    else
        return LITE_QUEUE_NONEMPTY;
}

/*
* @ brief : Determine if the queue is full.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ author: bagy.
* @ note  : Determine whether the queue is full by comparing the number of remaining unread list items with the total list items 
            of the queue. If they are the same, it means the queue is full, otherwise there is still space left in the queue.
*/
inline LiteQueue_Status isLiteQueue_Full(LiteQueue *queue)
{
    if(queue == NULL)
        return LITE_QUEUE_ERR;

    if(queue->unread_item == queue->item_num)
        return LITE_QUEUE_FULL;
    else
        return LITE_QUEUE_NONEFULL;
}

/*
* @ brief : Clear the message queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ author: bagy.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same, 
            it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
LiteQueue_Status LiteQueue_Clear(LiteQueue *queue)
{   
    if(queue == NULL)
        return LITE_QUEUE_ERR;

    LITE_QUEUE_WRITE_LOCK(queue);
    LITE_QUEUE_READ_LOCK(queue);

    queue->head = (uint8_t *)((uint8_t *)queue + sizeof(LiteQueue));
    queue->tail = queue->head;
    queue->unread_item = 0;

    memset(queue->head, 0, queue->item_num * queue->item_size);

    LITE_QUEUE_WRITE_UNLOCK(queue);
    LITE_QUEUE_READ_UNLOCK(queue);

    return LITE_QUEUE_OK;
}

/*
* @ brief : Clear the message queue.
* @ param : {LiteQueue      *} queue: Message queue handle pointer.
* @ return: {LiteQueue_Status} Returns the status of the queue.
* @ author: bagy.
* @ note  : Determine whether the head and tail pointers are the same. If they are the same, 
            it means there is no data in the queue, otherwise it means there is still data that has not been read out.
*/
LiteQueue_Status LiteQueue_Delete(LiteQueue *queue)
{
    if(queue == NULL)
        return LITE_QUEUE_ERR;    

    LITE_QUEUE_WRITE_LOCK(queue);
    LITE_QUEUE_READ_LOCK(queue);

    memset((uint8_t *)queue, 0, sizeof(LiteQueue) + queue->item_num * queue->item_size);

    queue->head = NULL;
    queue->tail = NULL;
    queue->LiteQueue_Callback = NULL;
    
    free(queue);
    queue = NULL;

    return LITE_QUEUE_OK;
}


